package com.fang.mmy.items.service.lmpl;

import com.fang.mmy.entity.ResponseResult;
import com.fang.mmy.entity.User;
import com.fang.mmy.items.config.EngineConfig;
import com.fang.mmy.items.constants.Constants;
import com.fang.mmy.items.entity.*;
import com.fang.mmy.items.feign.UserFeign;
import com.fang.mmy.items.filter.LogicFilter;
import com.fang.mmy.items.mapper.ItemMapper;
import com.fang.mmy.items.mapper.ItemNotesMapper;
import com.fang.mmy.items.mapper.ItemOnceNotesMapper;
import com.fang.mmy.items.mapper.ItemRuleDetailMapper;
import com.fang.mmy.items.service.ItemService;
import com.fang.mmy.utils.StringUtils;
import com.google.common.collect.Sets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @ClassName ItemServcicelmpl
 * @Author kebukeyi
 * @Date 2022/8/7 9:51
 * @Description
 * @Version 1.0.0
 */
@Service
public class ItemServicelmpl implements ItemService {

    @Autowired
    private ItemMapper itemMapper;

    @Autowired
    private ItemOnceNotesMapper itemOnceNotesMapper;

    @Autowired
    private ItemNotesMapper itemNotesMapper;

    @Autowired
    private ItemRuleDetailMapper ruleDetailMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private UserFeign userFeign;

    @Override
    public String getItemNo(Long userId) {
        if (userId == 123456L) {
            return "O111222333444";
        }
        return "订单不存在";
    }


    //登录任务（每个人默认都有，一周登录几天，算奖励再说）
    public boolean loginIn(Long userId, Long activityId, Long itemId) {

        return false;
    }

    //新建任务：每个用户需要 实时 | 延迟 看到
    //1. 可以设置成 凌晨1点进行预热，把昨天登录过的用户，再次初始化一次到redis中，新增任务可以，可以把任务开始时间设置到明天凌晨一点之前有效
    //2. 把每天新增的任务ID，选择保存在redis中，每次一个用户来了就尝试从中获取到，进行匹配判断
    @Transactional
    public boolean addItem() {
        Item item = new Item();
        item.setActivityId(100001L);
        //任务所需完成次数
        item.setItemNum(1);
        // 签到任务类型
        item.setItemTypeCode(99);
        // 全部人群
        item.setItemRuleCode(1);
        // 通过有效状态
        item.setState((byte) 4);
        item.setItemName("测试新建任务");
        itemMapper.insert(item);
        Long itemId = item.getId();
        redisTemplate.opsForSet().add(Constants.Today_Item, String.valueOf(itemId));
        ItemRuleDetail ruleDetail = new ItemRuleDetail();
        ruleDetail.setItemId(String.valueOf(itemId));
        ruleDetailMapper.insert(ruleDetail);
        return true;
    }

    //手动签到任务
    public boolean signIn(Long userId, Long activityId, Long itemId) {
        //先从redis中获得
        String signln = (String) redisTemplate.opsForHash().get(StringUtils.link(activityId, userId), String.valueOf(itemId));
        if (signln != null) {
            int parseInt = Integer.parseInt(signln);
            //完成过了
            if (parseInt == 1) {
                return false;
            }
        } else {
            //再去查库获得
            Item item = itemMapper.selectByPrimaryKey(itemId);
            ItemVo itemOver = isItemOver(userId, item);
            if (itemOver.isOk()) {
                //已经完成过了
                return false;
            }
        }

        //去完成
        Item item = itemMapper.selectByPrimaryKey(itemId);
        Integer itemReward = item.getItemReward();
        // 执行完成任务操作
        itemNotesMapper.insert(new ItemNotes(userId, itemId, 1, itemReward, (byte) 1));

        redisTemplate.opsForHash().put(StringUtils.link(activityId, userId), String.valueOf(item.getId()), String.valueOf(1));

        redisTemplate.opsForHash().increment(StringUtils.link(activityId, userId) + "_num", "finishedNum", 1);
        redisTemplate.opsForHash().increment(StringUtils.link(activityId, userId) + "_num", "noFinishedNum", -1);
        redisTemplate.opsForHash().increment(StringUtils.link(activityId, userId) + "_num", "finishedReward", itemReward);
        redisTemplate.opsForHash().increment(StringUtils.link(activityId, userId) + "_num", "noFinishedReward", -itemReward);
        return true;
    }

    //首页展示全部任务列表接口（未分类，分类字段：itemTypeCode：任务类型：100 签到，101分享任务，102考试学习，103政策通知，104活动任务，105日常任务）
    public List<ItemVo> displayList(Long activityId, Long userId) {
        //检查内存中是否存在 当前用户在当前活动中的任务领取
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(StringUtils.link(activityId, userId));
        if (entries.size() > 0) {
            //在将redis的数据返回时，尝试再判断一次，是否存在新任务添加
            return redisValueToList(activityId, userId, entries);
        }
        //
        List<ItemVo> checkFromDB = checkFromDB(activityId, userId);
        //存入缓存中
        itemListToRedis(activityId, userId, checkFromDB);
        return checkFromDB;
    }

    //筛选出匹配的任务+任务是否完成
    public List<ItemVo> checkFromDB(Long activityId, Long userId) {
        //远程调用
        ResponseResult<User> userResponseResult = userFeign.getUserNo((userId));
        User user = userResponseResult.getData();
        //获得匹配任务
        List<Item> items = filterItems(user, activityId);
        List<ItemVo> itemVos = new ArrayList<>();
        //逐一判断是否完成任务
        for (Item item : items) {
            ItemVo itemOver = isItemOver(userId, item);
            itemVos.add(itemOver);
        }
        return itemVos;
    }

    public List<ItemVo> redisValueToList(Long activityId, Long userId, Map<Object, Object> map) {
        Set<Object> keySet = map.keySet();
        // 需要每日
        Set<String> members = redisTemplate.opsForSet().members(Constants.Today_Item);
        //取差集 看是否存在新增任务
        Set<?> subtract = getSubtract(keySet, members);
        // 说明存在 今日 新增的任务
        if (subtract.size() > 0) {
            //过滤出 匹配的  新添加的任务，此时任务是未完成状态的，需要知道任务的一些详情
            Set<Object> itemIds = checkItemsFromDB(userId, subtract);
            for (Object itemId : itemIds) {
                map.put(itemId, "0");
                redisTemplate.opsForHash().put(StringUtils.link(activityId, userId), String.valueOf(itemId), "0");
            }
        }
        //无新增任务 继续走 redis 缓存
        ArrayList<ItemVo> itemVos = new ArrayList<>();
        for (Object itemId : map.keySet()) {
            //新添加的任务Id 为空的情况下，置为默认 "0"
            Object isOver = map.getOrDefault(itemId, "0");
            boolean a = Integer.parseInt((String) isOver) == 1;
            ItemVo itemVo = new ItemVo(Long.valueOf((String) itemId), a);
            itemVos.add(itemVo);
        }
        return itemVos;
    }

    private Set<Object> checkItemsFromDB(Long userId, Set<?> subtract) {
        Set<Object> set = new LinkedHashSet<>();
        ResponseResult<User> userNo = userFeign.getUserNo(userId);
        User user = userNo.getData();
        for (Object itemId : subtract) {
            Item item = itemMapper.selectByPrimaryKey(Long.valueOf((String) itemId));
            if (isMatchingItemToUser(user, item)) {
                set.add(itemId);
            }
        }
        return set;
    }

    public void itemListToRedis(Long activityId, Long userId, List<ItemVo> objects) {
        getItemNoOverVo(activityId, userId, objects);
    }

    // 统计未完成的 任务量 以及 积分值
    // 统计已完成的 任务量 以及 积分值
    // 并把每次的任务完成情况放入 redis 中
    public ItemOverVo getItemNoOverVo(Long activityId, Long userId, List<ItemVo> itemVos) {
        int finishedNum = 0;
        int noFinishedNum = 0;
        int finishedReward = 0;
        int noFinishedReward = 0;
        for (ItemVo itemVo : itemVos) {
            if (itemVo.isOk()) {
                finishedNum++;
                finishedReward = finishedReward + itemVo.getAward();
                redisTemplate.opsForHash().put(StringUtils.link(activityId, userId), String.valueOf(itemVo.getItemId()), String.valueOf(1));
            } else {
                noFinishedNum++;
                noFinishedReward = noFinishedReward + itemVo.getAward();
                redisTemplate.opsForHash().put(StringUtils.link(activityId, userId), String.valueOf(itemVo.getItemId()), String.valueOf(0));
            }
        }
        redisTemplate.opsForHash().put(StringUtils.link(activityId, userId) + "_num", "finishedNum", String.valueOf(finishedNum));
        redisTemplate.opsForHash().put(StringUtils.link(activityId, userId) + "_num", "noFinishedNum", String.valueOf(noFinishedNum));
        redisTemplate.opsForHash().put(StringUtils.link(activityId, userId) + "_num", "finishedReward", String.valueOf(finishedReward));
        redisTemplate.opsForHash().put(StringUtils.link(activityId, userId) + "_num", "noFinishedReward", String.valueOf(noFinishedReward));
        return new ItemOverVo(noFinishedNum, noFinishedReward, finishedNum, finishedReward);
    }

    //实时匹配任务（可能会有人员在后台实时添加任务）
    public List<Item> filterItems(User user, Long activityId) {
        List<Item> allValidByActivityId = getAllValidByActivityId(activityId);
        ArrayList<Item> arrayList = new ArrayList<>();
        // 匹配上的任务
        for (Item item : allValidByActivityId) {
            if (isMatchingItemToUser(user, item)) {
                arrayList.add(item);
            }
        }
        return arrayList;
    }

    //判断当前任务是否匹配上 人物
    public boolean isMatchingItemToUser(User user, Item item) {
        //任务规则类型：1（全部人群），2（指定账户），3（指定条件），4（制定其余条件）
        Integer itemRuleCode = item.getItemRuleCode();
        List<LogicFilter> logicFilters = EngineConfig.logicFilterMap.get(String.valueOf(itemRuleCode));
        for (LogicFilter logicFilter : logicFilters) {
            boolean filter = logicFilter.filter(user, item);
            if (filter) return true;
        }
        return false;
    }

    //判断当前任务 任务是否完成
    public ItemVo isItemOver(Long userId, Item item) {
        Integer itemTypeCode = item.getItemTypeCode();
        ItemVo itemVo = new ItemVo(item.getId(), item.getItemName(), item.getItemDesc(), false, item.getItemReward(), item.getItemTypeCode(), null, null);
        //一次任务类型 (不需要分库分表)
        if (itemTypeCode == 99) {
            List<ItemOnceNotes> andUserId = itemOnceNotesMapper.getAllByItemIdAndUserId(String.valueOf(userId), item.getId());
            //任务完成
            if (andUserId != null && andUserId.size() >= 1) {
                itemVo.setOk(true);
            } else {
                itemVo.setOk(false);
            }
        }
        //周期性任务（需要分库分表）根据用户Id
        else {
            //任务所需次数
            Integer itemNum = item.getItemNum();
            //todo 分库分表 key
            List<ItemNotes> itemNotes = itemNotesMapper.getAllByItemIdAndUserId(String.valueOf(userId), item.getId());
            //已完成次数
            int progress = itemNotes.size();
            if (progress < itemNum) {
                itemVo.setOk(false);
            } else if (itemNotes.size() == itemNum) {
                itemVo.setOk(true);
            }
        }
        return itemVo;
    }

    //获得当前活动下所有 验证通过的任务
    public List<Item> getAllValidByActivityId(Long activityId) {
        return itemMapper.getAllValidByActivityId(activityId);
    }

    /**
     * 求 set2 中的新值
     * set1：1，2，3，4
     * set2：3，4
     * set2：5，6
     */
    private static Set<?> getSubtract(Set<?> set1, Set<?> set2) {
        if (set2 == null) return null;
        Set<Object> set = Sets.newHashSet();
        set.addAll(set1);
        set.addAll(set2);
        set.removeAll(set1);
        return set;
    }

    public static void main(String[] args) {
        HashMap<Object, Object> map = new HashMap<>();
        Object isOver = map.getOrDefault(1, "0");
        boolean a = Integer.parseInt((String) isOver) == 1;
        System.out.println(a);
    }
}
